Finished integration tests
authorJeroen van der Heijden <jeroen@transceptor.technology>
Fri, 5 Oct 2018 20:44:24 +0000 (22:44 +0200)
committerJeroen van der Heijden <jeroen@transceptor.technology>
Fri, 5 Oct 2018 20:44:24 +0000 (22:44 +0200)
13 files changed:
itest/.dockerignore [new file with mode: 0644]
itest/Dockerfile
itest/run_all.py
itest/test_group.py
itest/test_list.py
itest/test_select.py
itest/test_series.py
itest/test_server.py
itest/test_user.py
itest/testing/__init__.py
itest/testing/args.py [new file with mode: 0644]
itest/testing/server.py
itest/testing/testbase.py

diff --git a/itest/.dockerignore b/itest/.dockerignore
new file mode 100644 (file)
index 0000000..e5482ab
--- /dev/null
@@ -0,0 +1,4 @@
+testdir/
+Dockerfile
+*.log
+__pycache__/
index 21b0473dbf8304d65ebeb9c9a8fba02c30aa6298..d202d61357e4d2503488b0ed54cdfab4f9f1044c 100644 (file)
@@ -1,4 +1,4 @@
-FROM ubuntu:18.04
+FROM ubuntu:18.04 as builder
 COPY ./main.c ./main.c
 COPY ./src/ ./src/
 COPY ./include/ ./include/
@@ -16,9 +16,15 @@ RUN cd ./Release && \
 
 FROM python
 RUN apt-get update && \
-    apt-get install -y valgrind
-COPY --from=0 ./Release/siridb-server /Release/siridb-server
+    apt-get install -y \
+        valgrind \
+        libuv1 \
+        libpcre2-8-0 && \
+    wget https://github.com/SiriDB/siridb-admin/releases/download/1.1.3/siridb-admin_1.1.3_linux_amd64.bin -O /usr/local/bin/siridb-admin && \
+    chmod +x /usr/local/bin/siridb-admin
+COPY --from=builder ./Release/siridb-server /Release/siridb-server
+COPY --from=builder /usr/lib/x86_64-linux-gnu/libcleri* /usr/lib/x86_64-linux-gnu/
 COPY ./itest/ /itest/
 WORKDIR /itest
 RUN pip install -r requirements.txt
-CMD [ "python", "test_select.py" ]
\ No newline at end of file
+CMD [ "python", "run_all.py", "-m", "-b=Release" ]
index 9ea0bfa7d21dad4f1d1236493866eb6c3c6cd037..344c1d9b297ac17c6484c2719fc5f19980229ebd 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/python3
 from testing import run_test
 from testing import Server
+from testing import parse_args
 from test_cluster import TestCluster
 from test_group import TestGroup
 from test_list import TestList
@@ -17,10 +18,8 @@ from test_pipe_support import TestPipeSupport
 from test_buffer import TestBuffer
 
 
-Server.BUILDTYPE = 'Release'
-
 if __name__ == '__main__':
-    # run_test(TestCluster())
+    parse_args()
     run_test(TestCompression())
     run_test(TestGroup())
     run_test(TestList())
index 4a5368701bbd7b2f4bb2d2a93e092783f1f6f7fe..3e07edf83518b779bc0d7c6303c2ff0ac97adbb0 100644 (file)
@@ -36,17 +36,17 @@ class TestGroup(TestBase):
     async def run(self):
         await self.client0.connect()
 
-        with self.assertRaisesRegexp(
+        with self.assertRaisesRegex(
                 QueryError,
                 'Group name should be at least 1 characters.'):
             await self.client0.query('create group `` for /c.*/')
 
-        with self.assertRaisesRegexp(
+        with self.assertRaisesRegex(
                 QueryError,
                 'Group name should be at least 1 characters.'):
             await self.client0.query('create group `` for /c.*/')
 
-        with self.assertRaisesRegexp(
+        with self.assertRaisesRegex(
                 QueryError,
                 'Group name should be at most [0-9]+ characters.'):
             await self.client0.query(
@@ -56,7 +56,7 @@ class TestGroup(TestBase):
             await self.client0.query('create group `a` for /a.*/'),
             {'success_msg': "Successfully created group 'a'."})
 
-        with self.assertRaisesRegexp(
+        with self.assertRaisesRegex(
                 QueryError,
                 'Group \'a\' already exists.'):
             await self.client0.query('create group `a` for /a.*/')
@@ -94,7 +94,7 @@ class TestGroup(TestBase):
         result = await self.client1.query('list groups series')
         self.assertEqual(result.pop('groups'), [[2], [2], [2]])
 
-        with self.assertRaisesRegexp(
+        with self.assertRaisesRegex(
                 QueryError,
                 "Cannot compile regular expression.*"):
             result = await self.client1.query('create group `invalid` for /(/')
@@ -137,7 +137,7 @@ class TestGroup(TestBase):
         result = await self.client0.query('list series `a`, `two` & "c2"')
         self.assertEqual(sorted(result.pop('series')), [['c2']])
 
-        with self.assertRaisesRegexp(
+        with self.assertRaisesRegex(
                 QueryError,
                 "Cannot compile regular expression.*"):
             await self.client1.query('alter group `a` set expression /(.*/')
@@ -150,7 +150,7 @@ class TestGroup(TestBase):
         # await self.client0.query('drop group `b`')
         await self.client0.query('drop group `c`')
 
-        with self.assertRaisesRegexp(
+        with self.assertRaisesRegex(
                 QueryError,
                 'Group \'c\' does not exist.'):
             await self.client0.query('drop group `c`')
index 154c0fa829237744209a84f37efcfa8cd57430de..5c6f9f22e9bbffe9b255eb7f776155b0fb7f6353 100644 (file)
@@ -94,7 +94,7 @@ class TestList(TestBase):
         await self.client0.query('list series /.*/ ^ /a.*/ ^ /.*/')
 
         await self.client0.query('alter database set list_limit 5000')
-        with self.assertRaisesRegexp(
+        with self.assertRaisesRegex(
                 QueryError,
                 'Limit must be a value between 0 and 5000 '
                 'but received: 6000.*'):
index 3d9df9dcbb6c1a834c3c3b4e33e4f8edd4e7b916..d2070b88d04a10a380dfdaa1a07c1a0b88995782 100644 (file)
@@ -19,6 +19,7 @@ from testing import ServerError
 from testing import SiriDB
 from testing import TestBase
 from testing import UserAuthError
+from testing import parse_args
 
 
 DATA = {
@@ -338,82 +339,82 @@ class TestSelect(TestBase):
             await self.client0.query('select difference() from "one"'),
             {'one': []})
 
-        with self.assertRaisesRegexp(
+        with self.assertRaisesRegex(
                 QueryError,
                 'Regular expressions can only be used with.*'):
             await self.client0.query('select filter(~//) from "log"')
 
-        with self.assertRaisesRegexp(
+        with self.assertRaisesRegex(
                 QueryError,
                 'Cannot use a string filter on number type.'):
             await self.client0.query('select filter(//) from "aggr"')
 
-        with self.assertRaisesRegexp(
+        with self.assertRaisesRegex(
                 QueryError,
                 'Cannot use mean\(\) on string type\.'):
             await self.client0.query('select mean(1w) from "log"')
 
-        with self.assertRaisesRegexp(
+        with self.assertRaisesRegex(
                 QueryError,
                 'Group by time must be an integer value larger than zero\.'):
             await self.client0.query('select mean(0) from "aggr"')
 
-        with self.assertRaisesRegexp(
+        with self.assertRaisesRegex(
                 QueryError,
                 'Limit must be an integer value larger than zero\.'):
             await self.client0.query('select limit(6 - 6, mean) from "aggr"')
 
-        with self.assertRaisesRegexp(
+        with self.assertRaisesRegex(
                 QueryError,
                 'Cannot use a string filter on number type\.'):
             await self.client0.query(
                 'select * from "aggr" '
                 'merge as "t" using filter("0")')
 
-        with self.assertRaisesRegexp(
+        with self.assertRaisesRegex(
                 QueryError,
                 'Cannot use difference\(\) on string type\.'):
             await self.client0.query('select difference() from "log"')
 
-        with self.assertRaisesRegexp(
+        with self.assertRaisesRegex(
                 QueryError,
                 'Cannot use derivative\(\) on string type\.'):
             await self.client0.query('select derivative(6, 3) from "log"')
 
-        with self.assertRaisesRegexp(
+        with self.assertRaisesRegex(
                 QueryError,
                 'Cannot use derivative\(\) on string type\.'):
             await self.client0.query('select derivative() from "log"')
 
-        with self.assertRaisesRegexp(
+        with self.assertRaisesRegex(
                 QueryError,
                 'Overflow detected while using sum\(\)\.'):
             await self.client0.query('select sum(now) from "huge"')
 
-        with self.assertRaisesRegexp(
+        with self.assertRaisesRegex(
                 QueryError,
                 'Max depth reached in \'where\' expression!'):
             await self.client0.query(
                 'select * from "aggr" where ((((((length > 1))))))')
 
-        with self.assertRaisesRegexp(
+        with self.assertRaisesRegex(
                 QueryError,
                 'Cannot compile regular expression.*'):
             await self.client0.query(
                 'select * from /(bla/')
 
-        with self.assertRaisesRegexp(
+        with self.assertRaisesRegex(
                 QueryError,
                 'Memory allocation error or maximum recursion depth reached.'):
             await self.client0.query(
                 'select * from "aggr" where length > {}'.format('(' * 500))
 
-        with self.assertRaisesRegexp(
+        with self.assertRaisesRegex(
                     QueryError,
                     'Query too long.'):
             await self.client0.query('select * from "{}"'.format('a' * 65535))
 
-        with self.assertRaisesRegexp(
+        with self.assertRaisesRegex(
                     QueryError,
                     'Error while merging points. Make sure the destination '
                     'series name is valid.'):
@@ -476,7 +477,7 @@ class TestSelect(TestBase):
         self.assertIn('aggr-maximum', result)
 
         await self.client0.query('alter database set select_points_limit 10')
-        with self.assertRaisesRegexp(
+        with self.assertRaisesRegex(
                 QueryError,
                 'Query has reached the maximum number of selected points.*'):
             await self.client0.query(
@@ -490,9 +491,5 @@ class TestSelect(TestBase):
 
 
 if __name__ == '__main__':
-    SiriDB.LOG_LEVEL = 'CRITICAL'
-    Server.HOLD_TERM = True
-    Server.MEM_CHECK = True
-    Server.TERMINAL = 'XTERM'
-    Server.BUILDTYPE = 'Debug'
+    parse_args()
     run_test(TestSelect())
index 1bf42eb152723f07beba9398af1269a4b7515367..42c6d750a6765e814fa85fe216507eece81e5ee9 100644 (file)
@@ -33,7 +33,7 @@ data = {
     'integer': [
         [1538660000, 1],
         [1538660010, 35.6],
-        [1538660020, "-50,6%"],
+        [1538660020, "-50%"],
         [1538660030, ""],
         [1538660035, "garbage"],
         [1538660040, "18446744073709551616"],
@@ -42,7 +42,7 @@ data = {
     'double': [
         [1538660000, 1.0],
         [1538660010, -35],
-        [1538660010, "-50,6%"],
+        [1538660010, "-50%"],
         [1538660030, ""],
         [1538660035, "garbage"],
     ]
@@ -67,9 +67,9 @@ expected = {
     'double': [
         [1538660000, 1.0],
         [1538660010, -35.0],
-        [1538660010, -50.6],
+        [1538660010, -50.0],
         [1538660030, 0.0],
-        [1538660035, 0],
+        [1538660035, 0.0],
     ]
 }
 
index 713b0d6948a1b8845ff94382a6045c18f56f0484..62cac4ee4456f1ad257822c071e550e17dc76bc6 100644 (file)
@@ -75,7 +75,7 @@ class TestServer(TestBase):
         result = await self.client1.query('list servers log_level')
         self.assertEqual(result.pop('servers'), [['debug'], ['debug']])
 
-        with self.assertRaisesRegexp(
+        with self.assertRaisesRegex(
                 QueryError,
                 "Query error at position 42. Expecting "
                 "debug, info, warning, error or critical"):
@@ -103,13 +103,13 @@ class TestServer(TestBase):
         await self.db.add_replica(self.server2, 1)
         await self.assertIsRunning(self.db, self.client0, timeout=10)
 
-        with self.assertRaisesRegexp(
+        with self.assertRaisesRegex(
                 QueryError,
                 "Cannot remove server 'localhost:9010' "
                 "because this is the only server for pool 0"):
             await self.client1.query('drop server "localhost:9010"')
 
-        with self.assertRaisesRegexp(
+        with self.assertRaisesRegex(
                 QueryError,
                 "Cannot remove server 'localhost:9012' "
                 "because the server is still online.*"):
index 853de74fa7b1ae4b21773d0f4665d54d78a9abc4..505380ba5d3f861ebe41212384bc81d10576d5f8 100644 (file)
@@ -32,17 +32,17 @@ class TestUser(TestBase):
         with self.assertRaises(QueryError):
             await self.client0.query('create user "sasientje" ')
 
-        with self.assertRaisesRegexp(
+        with self.assertRaisesRegex(
                 QueryError,
                 'User name should be at least 2 characters.'):
             await self.client0.query('create user "s" set password "123456" ')
 
-        with self.assertRaisesRegexp(
+        with self.assertRaisesRegex(
                 QueryError,
                 'User name contains illegal characters.*'):
             await self.client0.query('create user "  " set password "123456" ')
 
-        with self.assertRaisesRegexp(
+        with self.assertRaisesRegex(
                 QueryError,
                 'Password should be at least 4 characters.'):
             await self.client0.query('create user "aa" set password "123" ')
@@ -97,7 +97,7 @@ class TestUser(TestBase):
         result = await self.server0.stop()
         self.assertTrue(result)
 
-        with self.assertRaisesRegexp(
+        with self.assertRaisesRegex(
                 QueryError,
                 "Password should be at least 4 characters."):
             result = await self.client1.query(
@@ -118,7 +118,7 @@ class TestUser(TestBase):
         result = await self.client0.query("show who_am_i ")
         self.assertEqual(result['data'][0]['value'], 'sasientje')
 
-        with self.assertRaisesRegexp(
+        with self.assertRaisesRegex(
                 UserAuthError,
                 "Access denied. User 'sasientje' has no 'insert' privileges."):
             result = await self.client0.insert({'no access test': [[1, 1.0]]})
@@ -137,35 +137,35 @@ class TestUser(TestBase):
         result = await self.client0.query('count users where name == "pee"')
         self.assertEqual(result.pop('users'), 1, msg='Expecting 1 user (pee)')
 
-        with self.assertRaisesRegexp(
+        with self.assertRaisesRegex(
                 UserAuthError,
                 "Access denied. User 'sasientje' has no 'grant' privileges."):
             result = await self.client0.query('grant full to user "pee" ')
 
-        with self.assertRaisesRegexp(
+        with self.assertRaisesRegex(
                 QueryError,
                 "User name should be at least 2 characters."):
             result = await self.client1.query('alter user "pee" set name "p" ')
 
-        with self.assertRaisesRegexp(
+        with self.assertRaisesRegex(
                 QueryError,
                 "^User name contains illegal characters.*"):
             result = await self.client1.query(
                 'alter user "pee" set name " p " ')
 
-        with self.assertRaisesRegexp(
+        with self.assertRaisesRegex(
                 QueryError,
                 "User 'iris' already exists."):
             result = await self.client1.query(
                 'alter user "pee" set name "iris" ')
 
-        with self.assertRaisesRegexp(
+        with self.assertRaisesRegex(
                 QueryError,
                 "User 'iris' already exists."):
             result = await self.client1.query(
                 'alter user "pee" set name "iris" ')
 
-        with self.assertRaisesRegexp(
+        with self.assertRaisesRegex(
                 QueryError,
                 "Cannot find user: 'Pee'"):
             result = await self.client1.query(
index fe23f73f4f7ffb1e296a9650a7c6cb1997c50cb6..282c8ff67c367f8dce02dc24a8c599a7c7bbd6bf 100644 (file)
@@ -18,26 +18,74 @@ from .testbase import default_test_setup
 from .testbase import TestBase
 from .series import Series
 from .pipe_client import PipeClient as SiriDBAsyncUnixConnection
+from .args import parse_args
+
+SPINNER1 = \
+    ('▁', '▂', '▃', '▄', '▅', '▆', '▇', '█', '▇', '▆', '▅', '▄', '▃', '▁')
+SPINNER2 = \
+    ('⠁', '⠂', '⠄', '⡀', '⢀', '⠠', '⠐', '⠈')
+SPINNER3 = \
+    ('◐', '◓', '◑', '◒')
+
+
+class Spinner():
+
+    def __init__(self, charset=SPINNER3):
+        self._idx = 0
+        self._charset = charset
+        self._len = len(charset)
+
+    @property
+    def next(self):
+        char = self._charset[self._idx]
+        self._idx += 1
+        self._idx %= self._len
+        return char
+
+
+class Task():
+    def __init__(self, title):
+        self.running = True
+        self.task = asyncio.ensure_future(self.process())
+        self.success = False
+        self.title = title
+        self.start = time.time()
+
+    def stop(self, success):
+        self.running = False
+        self.success = success
+        self.duration = time.time() - self.start
+
+    async def process(self):
+        spinner = Spinner()
+        while self.running:
+            sys.stdout.write(f'{self.title:.<76}{spinner.next}\r')
+            sys.stdout.flush()
+            await asyncio.sleep(0.2)
+
+        if self.success:
+            print(f'{self.title:.<76}OK ({self.duration:.2f} seconds)')
+        else:
+            print(f'{self.title:.<76}FAILED ({self.duration:.2f} seconds)')
 
 
 async def _run_test(test, loglevel):
     logger = logging.getLogger()
     logger.setLevel(loglevel)
-
-    start = time.time()
-    print('{:.<76}'.format(test.title), end='')
-    sys.stdout.flush()
+    task = Task(test.title)
 
     try:
         await test.run()
     except Exception as e:
-        print('FAILED ({:.2f} seconds)'.format(time.time() - start))
+        task.stop(success=False)
         raise e
     else:
-        print('OK ({:.2f} seconds)'.format(time.time() - start))
+        task.stop(success=True)
 
     logger.setLevel('CRITICAL')
 
+    await task.task
+
 
 def run_test(test, loglevel='CRITICAL'):
     assert isinstance(test, TestBase)
diff --git a/itest/testing/args.py b/itest/testing/args.py
new file mode 100644 (file)
index 0000000..3a7506a
--- /dev/null
@@ -0,0 +1,44 @@
+import argparse
+from .server import Server
+
+
+def parse_args():
+    parser = argparse.ArgumentParser()
+
+    parser.add_argument(
+        '-t', '--terminal',
+        choices=['xterm', 'xfce4-terminal'],
+        default=None,
+        help='Start SiriDB servers in a terminal. If no terminal is given '
+        'process put their output in log files.')
+
+    parser.add_argument(
+        '-m', '--mem-check',
+        action='store_true',
+        help='Use `valgrind` for memory errors and leaks.')
+
+    parser.add_argument(
+        '-k', '--keep',
+        action='store_true',
+        help='Only valid when a terminal is used. This will keep the terminal '
+        'open.')
+
+    parser.add_argument(
+        '-b', '--build',
+        choices=['Release', 'Debug'],
+        default='Release',
+        help='Choose either the Release or Debug build.')
+
+    parser.add_argument(
+        '-l', '--log-level',
+        default='critical',
+        help='set the log level',
+        choices=['debug', 'info', 'warning', 'error', 'critical'])
+
+    args = parser.parse_args()
+
+    Server.MEM_CHECK = args.mem_check
+    Server.HOLD_TERM = args.keep
+    Server.TERMINAL = args.terminal
+    Server.BUILDTYPE = args.build
+    Server.LOG_LEVEL = args.log_level.upper()
index ca21e46c0042251063e2943eb8ced150eda0ec0b..2b9b4b313c74c880f61d26c7e60b6403712db267 100644 (file)
@@ -31,6 +31,7 @@ class Server:
 
     def __init__(self,
                  n,
+                 title,
                  optimize_interval=300,
                  heartbeat_interval=30,
                  buffer_sync_interval=500,
@@ -38,6 +39,7 @@ class Server:
                  pipe_name=None,
                  **unused):
         self.n = n
+        self.test_title = title.lower().replace(' ', '_')
         self.compression = compression
         self.enable_pipe_support = int(bool(pipe_name))
         self.pipe_name = \
@@ -106,9 +108,8 @@ class Server:
 
     async def start(self, sleep=None):
         prev = self._get_pid_set()
-
-        if self.TERMINAL == 'XFCE4_TERMINAL':
-            rc = subprocess.Popen(
+        if self.TERMINAL == 'xfce4-terminal':
+            self.proc = subprocess.Popen(
                 'xfce4-terminal -e "{}{} --config {} --log-colorized"'
                 ' --title {} --geometry={}{}'
                 .format(VALGRIND if self.MEM_CHECK else '',
@@ -118,8 +119,8 @@ class Server:
                         self.GEOMETRY,
                         ' -H' if self.HOLD_TERM else ''),
                 shell=True)
-        elif self.TERMINAL == 'XTERM':
-            rc = subprocess.Popen(
+        elif self.TERMINAL == 'xterm':
+            self.proc = subprocess.Popen(
                'xterm {}-title {} -geometry {} -e "{}{} --config {}"'
                .format('-hold ' if self.HOLD_TERM else '',
                        self.name,
@@ -129,9 +130,13 @@ class Server:
                        self.cfgfile),
                shell=True)
         else:
-            with open(f'{self.name}-err.log', 'a') as err:
-                with open(f'testdir/{self.name}-out.log', 'a') as out:
-                    rc = subprocess.Popen(
+            with open(
+                    f'testdir/{self.test_title}-{self.name}-err.log',
+                    'a') as err:
+                with open(
+                        f'testdir/{self.test_title}-{self.name}-out.log',
+                        'a') as out:
+                    self.proc = subprocess.Popen(
                         '{}{} --config {}'
                         .format(VALGRIND if self.MEM_CHECK else '',
                                 SIRIDBC.format(BUILDTYPE=self.BUILDTYPE),
@@ -139,7 +144,6 @@ class Server:
                         stderr=err,
                         stdout=out,
                         shell=True)
-                    self.assertEqual(rc, 0)
 
         await asyncio.sleep(5)
 
@@ -157,6 +161,9 @@ class Server:
                 await asyncio.sleep(1.0)
                 timeout -= 1
 
+        self.proc.communicate()
+        assert (self.proc.returncode == 0)
+
         if timeout:
             self.pid = None
             return True
index dfba3d2b8c8065a48ba605ca0d1bba140ebdab52..8008e65f38aaf8f73def5d1138310e97741c38d9 100644 (file)
@@ -14,7 +14,8 @@ def default_test_setup(nservers=1, **kwargs):
         async def wrapped(self):
             self.db = SiriDB(**kwargs)
 
-            self.servers = [Server(n, **kwargs) for n in range(nservers)]
+            self.servers = [
+                Server(n, title=self.title, **kwargs) for n in range(nservers)]
             for n, server in enumerate(self.servers):
                 setattr(self, 'server{}'.format(n), server)
                 setattr(self, 'client{}'.format(n), Client(self.db, server))
@@ -117,7 +118,9 @@ class TestBase(unittest.TestCase):
                         'Expecting a point to be a list of 2 items'
                     super().assertEqual(a[series][i][0], point[0])
                     if isinstance(a[series][i][1], str):
-                        super().assertEqual(a[series][i][1], point[1])
+                        super().assertEqual(
+                            a[series][i][1].replace(',', '.'),
+                            point[1].replace(',', '.'))
                     elif math.isnan(a[series][i][1]):
                         assert math.isnan(point[1]), \
                             'Expecting point `{}` to be `nan`, got: `{}`' \